package com.lambo.maven.core;

import com.lambo.maven.core.utils.BizException;
import com.lambo.maven.core.utils.HelpUtil;
import com.lambo.maven.core.utils.HttpUtil;
import com.lambo.maven.core.utils.IoUtil;
import com.lambo.maven.core.vo.MavenRequest;
import com.lambo.maven.core.vo.MavenResponse;
import com.lambo.maven.core.vo.SettingConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Date;

/**
 * store.
 *
 * @author lambo
 * @date 2018/6/16
 */
public class MavenRepositoryStore {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final Charset DEFAULT_CHARSET_UTF8 = Charset.forName("UTF-8");

    private final SettingConfig settingConfig;

    public MavenRepositoryStore(SettingConfig settingConfig) {
        this.settingConfig = settingConfig;
    }

    /**
     * 处理请求.
     *
     * @param request 请求.
     * @return 响应.
     */
    public MavenResponse handle(MavenRequest request) {
        String requestPath = request.getPath();

        //只处理当前项目的请求.
        if (!requestPath.startsWith(settingConfig.httpContext)) {
            return new MavenResponse(404, getBytes("not found, url = " + requestPath));
        }
        String path = requestPath.substring(settingConfig.httpContext.length());
        String libName = path;
        //url第一部分为仓库id.
        if (path.contains("/")) {
            libName = path.substring(0, path.indexOf("/"));
        }
        path = path.substring(libName.length());
        if ("GET".equals(request.getMethod()) || "HEAD".equals(request.getMethod())) {
            byte[] data;
            try {
                data = load(libName, path, request, -1);
            } catch (Exception e) {
                logger.error("load failed, path = {}", path);
                return new MavenResponse(404, getBytes(e.getLocalizedMessage()));
            }
            if (null == data) {
                return new MavenResponse(404, getBytes("not found, lib = " + path));
            }
            return new MavenResponse(200, data);
        }
        if ("PUT".equals(request.getMethod())) {
            byte[] data = request.getBody();
            if (null == data || data.length == 0) {
                return new MavenResponse(404, getBytes("stream has error, lib = " + path));
            }
            try {
                save(libName, path, data);
            } catch (Exception e) {
                logger.error("save failed, path = {}", path, e);
                return new MavenResponse(500, getBytes(e.getLocalizedMessage()));
            }
            return new MavenResponse(200, getBytes("OK"));
        }
        return new MavenResponse(405, getBytes(request.getMethod() + " not support!"));
    }

    /**
     * 加载资源文件.
     *
     * @param libName jar文件.
     * @param path    路径.
     * @param request 请求.
     * @param mode    模式.
     * @return 资源.
     * @throws IOException 异常.
     */
    private byte[] load(String libName, String path, MavenRequest request, int mode) throws IOException {
        // 验证仓库是否可用.
        if (!settingConfig.activeProfiles.contains(libName)) {
            throw new BizException(404, "lib " + libName + " not active");
        }
        SettingConfig.Repository repository = settingConfig.getRepository(libName);
        //mode权限. -1表示未初始化.
        if (mode == -1) {
            mode = repository.mode;
        }
        // mode = 6为可读可写，mode = 4 为只读.
        if (mode != 6 && mode != 4) {
            throw new BizException(403, libName + " not support read.");
        }
        File localPath = new File(settingConfig.localRepository + "/" + libName + path);
        //是否为目录浏览.
        if (localPath.exists() && localPath.isDirectory()) {
            if (request.isGenerateMd5Sha1()) {
                HelpUtil.signFile(localPath);
            }
            return indexOf(localPath, request);
        }
        //是否为文件.
        if (localPath.exists() && localPath.isFile()) {
            byte[] bytes = IoUtil.readToByteBuffer(localPath);
            if (null == bytes) {
                return new byte[0];
            }
            return bytes;
        }
        // url 不为空时进行其它仓库转发.
        if (null != repository.url && !repository.url.isEmpty()) {
            for (String httpUrl : repository.url) {
                byte[] response = HttpUtil.doGet(httpUrl + path);
                if (null != response) {
                    IoUtil.writeFile(localPath.getAbsolutePath(), response);
                    return response;
                }
                logger.debug("download failed, url = {}{} ", httpUrl, path);
            }
        }
        //target不为空时，进行本地转发.
        if (null != repository.target && settingConfig.activeProfiles.contains(repository.target)) {
            // 转发到其它仓库执行.mode 也一并转发.
            return load(repository.target, path, request, repository.mode);
        }
        return null;
    }

    /**
     * 遍历目录生成页面.
     *
     * @param localPath 本地路径.
     * @param request   请求.
     * @return 页面数据.
     * @throws IOException 异常.
     */
    private byte[] indexOf(File localPath, MavenRequest request) throws IOException {
        String path = request.getPath();
        if (!path.endsWith("/")) {
            path += "/";
        }
        String demo = new String(IoUtil.readToByteBuffer("classpath:/maven.index.html"),
                DEFAULT_CHARSET_UTF8);
        File[] files = localPath.listFiles();
        StringBuilder dirContent = new StringBuilder();
        StringBuilder fileContent = new StringBuilder();
        if (null != files) {
            StringBuilder li = new StringBuilder();
            for (File file : files) {
                li.append("<li><span class=\"name\"><a href=\"").append(HelpUtil.encodePath2Href(file.getName())).
                        append("\">").append(file.getName()).append("</a></span><span class=\"modified\">").
                        append(HelpUtil.formatDateTime(new Date(file.lastModified()))).
                        append("</span><span class=\"length\">").
                        append(file.length()).
                        append("</span></li>");
                if (file.isDirectory()) {
                    dirContent.append(li);
                } else {
                    fileContent.append(li);
                }
                li.setLength(0);
            }
        }
        dirContent.append(fileContent);
        return getBytes(demo.replace("${path}", path).replace("${content}", dirContent.toString()));
    }

    /**
     * 字符串转字节.
     *
     * @param text 字符串.
     * @return 字节数组.
     */
    private byte[] getBytes(String text) {
        return null != text ? text.getBytes(DEFAULT_CHARSET_UTF8) : null;
    }

    /**
     * 保存jar文件.
     *
     * @param libName 原生jar文件.
     * @param path    位置.
     * @param data    jar包内容.
     */
    private void save(String libName, String path, byte[] data) {
        // 验证仓库是否可用.
        if (!settingConfig.activeProfiles.contains(libName)) {
            throw new BizException(404, "lib " + libName + " not active");
        }
        SettingConfig.Repository repository = settingConfig.getRepository(libName);
        // mode = 6为可读可写，mode = 2 为只写.
        if (repository.mode != 6 && repository.mode != 2) {
            throw new BizException(403, libName + " not support write.");
        }
        IoUtil.writeFile(settingConfig.localRepository + "/" + libName + path, data);
    }

    /**
     * 登录验证，主要用于上传jar包及生成md5签名.
     *
     * @param user 用户名.
     * @param pwd  密码.
     * @return 是否成功.
     */
    public boolean checkUser(String user, String pwd) {
        for (SettingConfig.User u : settingConfig.user) {
            if (u.name.equals(user) && u.password.equals(pwd)) {
                return true;
            }
        }
        return false;
    }
}
